home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 68K / Demo / www / Formatter.py < prev    next >
Text File  |  1996-05-20  |  9KB  |  341 lines

  1. # A more powerful formatter class.
  2.  
  3. # XXX Should change again -- the d_* functions should be methods of a
  4. # separate object passed at init() time, not of a derivced class...
  5.  
  6.  
  7. class AbstractFormatter:
  8.     #
  9.     # Font interface functions.  Override these to change the font
  10.     # interface.  (Defaults are for a character cell device.)
  11.     #
  12.     def d_setfont(self, font):
  13.         pass
  14.     #
  15.     def d_textwidth(self, text):
  16.         return len(text)
  17.     #
  18.     def d_baseline(self):
  19.         return 0
  20.     #
  21.     def d_lineheight(self):
  22.         return 1
  23.     #
  24.     # Output interface function.  Override this to define how
  25.     # characters are actually output.
  26.     #
  27.     def d_text(self, (h, v), text):
  28.         pass
  29.     #
  30.     # Initialize a formatter instance.
  31.     # Pass the left, top, right coordinates of the drawing space
  32.     # as arguments.  (There is no bottom argument -- the bottom of
  33.     # the text is computed as a side effect of formatting it.)
  34.     #
  35.     def init(self, left, top, right):
  36.         self.left = left    # Left margin, variable
  37.         self.leftmargin = left    # Left margin, fixed
  38.         self.right = right    # Right margin, variable
  39.         self.rightmargin = right # Right margin, fixed
  40.         self.v = top        # Top of current line
  41.         self.center = 0
  42.         self.justify = 0
  43.         self.setfont('')    # Default font
  44.         self._reset()        # Prepare for new line
  45.         return self
  46.     #
  47.     # Reset for start of fresh line.
  48.     #
  49.     def _reset(self):
  50.         self.boxes = []        # Boxes and glue still to be output
  51.         self.sum_width = 0    # Total width of boxes
  52.         self.sum_space = 0    # Total space between boxes
  53.         self.sum_stretch = 0    # Total stretch for space between boxes
  54.         self.max_ascent = 0    # Max ascent of current line
  55.         self.max_descent = 0    # Max descent of current line
  56.         self.avail_width = self.right - self.left
  57.         self.hang_indent = 0
  58.     #
  59.     # Set the current font, and compute some values from it.
  60.     #
  61.     def setfont(self, font):
  62.         self.font = font
  63.         self.d_setfont(font)
  64.         self.font_space = self.d_textwidth(' ')
  65.         self.font_ascent = self.d_baseline()
  66.         self.font_descent = self.d_lineheight() - self.font_ascent
  67.     #
  68.     # Set the current justification / centering flags.
  69.     #
  70.     def setjustify(self, flag):
  71.         self.justify = flag
  72.     #
  73.     def setcenter(self, flag):
  74.         self.center = flag
  75.     #
  76.     # Extract the bottom of the text.  Implies a flush.
  77.     #
  78.     def getbottom(self):
  79.         self.flush()
  80.         return self.v
  81.     #
  82.     # Add a word to the list of boxes; first flush if line is full.
  83.     # Space and stretch factors are expressed in fractions
  84.     # of the current font's space width.
  85.     # (Two variations: one without, one with explicit stretch factor.)
  86.     #
  87.     def addword(self, word, spacefactor):
  88.         self.addwordstretch(word, spacefactor, spacefactor)
  89.     #
  90.     def addwordstretch(self, word, spacefactor, stretchfactor):
  91.         width = self.d_textwidth(word)
  92.         if width > self.avail_width:
  93.             self._flush(1)
  94.         space = int(float(self.font_space) * float(spacefactor))
  95.         stretch = int(float(self.font_space) * float(stretchfactor))
  96.         box = (self.font, word, width, space, stretch)
  97.         self.boxes.append(box)
  98.         self.sum_width = self.sum_width + width
  99.         self.sum_space = self.sum_space + space
  100.         self.sum_stretch = self.sum_stretch + stretch
  101.         self.max_ascent = max(self.font_ascent, self.max_ascent)
  102.         self.max_descent = max(self.font_descent, self.max_descent)
  103.         self.avail_width = self.avail_width - width - space
  104.     #
  105.     # Flush current line and start a new one.
  106.     # Flushing twice is harmless (i.e. does not introduce a blank line).
  107.     # (Two versions: the internal one has a parameter for justification.)
  108.     #
  109.     def flush(self):
  110.         self._flush(0)
  111.     #
  112.     def _flush(self, justify):
  113.         if not self.boxes:
  114.             return
  115.         #
  116.         # Compute amount of stretch needed_
  117.         #
  118.         if justify and self.justify or self.center:
  119.             #
  120.             # Compute extra space to fill;
  121.             # this is avail_width plus glue from last box.
  122.             # Also compute available stretch.
  123.             #
  124.             last_box = self.boxes[len(self.boxes)-1]
  125.             font, word, width, space, stretch = last_box
  126.             tot_extra = self.avail_width + space
  127.             tot_stretch = self.sum_stretch - stretch
  128.         else:
  129.             tot_extra = tot_stretch = 0
  130.         #
  131.         # Output the boxes.
  132.         #
  133.         baseline = self.v + self.max_ascent
  134.         h = self.left + self.hang_indent
  135.         if self.center:
  136.             h = h + tot_extra / 2
  137.             tot_extra = tot_stretch = 0
  138.         for font, word, width, space, stretch in self.boxes:
  139.             self.d_setfont(font)
  140.             v = baseline - self.d_baseline()
  141.             self.d_text((h, v), word)
  142.             h = h + width + space
  143.             if tot_extra > 0 and tot_stretch > 0:
  144.                 extra = stretch * tot_extra / tot_stretch
  145.                 h = h + extra
  146.                 tot_extra = tot_extra - extra
  147.                 tot_stretch = tot_stretch - stretch
  148.         #
  149.         # Prepare for next line.
  150.         #
  151.         self.v = baseline + self.max_descent
  152.         self.d_setfont(self.font)
  153.         self._reset()
  154.     #
  155.     # Add vertical space; first flush.
  156.     # Vertical space is expressed in fractions of the current
  157.     # font's line height.
  158.     #
  159.     def vspace(self, lines):
  160.         self.vspacepixels(int(lines * self.d_lineheight()))
  161.     #
  162.     # Add vertical space given in pixels.
  163.     #
  164.     def vspacepixels(self, dv):
  165.         self.flush()
  166.         self.v = self.v + dv
  167.     #
  168.     # Set temporary (hanging) indent, for paragraph start.
  169.     # First flush.
  170.     #
  171.     def tempindent(self, space):
  172.         self.flush()
  173.         hang = int(float(self.font_space) * float(space))
  174.         self.hang_indent = hang
  175.         self.avail_width = self.avail_width - hang
  176.     #
  177.     # Set (permanent) left indentation.  First flush.
  178.     #
  179.     def setleftindent(self, space):
  180.         self.flush()
  181.         self.left = self.leftmargin + \
  182.             int(float(self.font_space) * float(space))
  183.         self._reset()
  184.     #
  185.     # Add (permanent) left indentation.  First flush.
  186.     #
  187.     def addleftindent(self, space):
  188.         self.flush()
  189.         self.left = self.left \
  190.             + int(float(self.font_space) * float(space))
  191.         self._reset()
  192.  
  193.  
  194. class WritingFormatter(AbstractFormatter):
  195.     #
  196.     def init(self, fp, linewidth):
  197.         self.fp = fp
  198.         self.lineno, self.colno = 0, 0
  199.         return AbstractFormatter.init(self, 0, 0, linewidth)
  200.     #
  201.     def d_text(self, (h, v), text):
  202.         if '\n' in text:
  203.             raise ValueError, 'no newline allowed in d_text'
  204.         while self.lineno < v:
  205.             self.fp.write('\n')
  206.             self.colno, self.lineno = 0, self.lineno + 1
  207.         if self.colno < h:
  208.             self.fp.write(' ' * (h - self.colno))
  209.         elif self.colno > h:
  210.             self.fp.write('\b' * (self.colno - h))
  211.         self.colno = h
  212.         self.fp.write(text)
  213.         self.colno = h + len(text)
  214.  
  215.  
  216. class StdwinFormatter(AbstractFormatter):
  217.     #
  218.     def init(self, d, left, top, right):
  219.         self.d = d
  220.         return AbstractFormatter.init(self, left, top, right)
  221.     #
  222.     def d_text(self, point, text):
  223.         self.d.text(point, text)
  224.     #
  225.     def d_setfont(self, font):
  226.         self.d.setfont(font)
  227.     #
  228.     def d_textwidth(self, text):
  229.         return self.d.textwidth(text)
  230.     #
  231.     def d_baseline(self):
  232.         return self.d.baseline()
  233.     #
  234.     def d_lineheight(self):
  235.         return self.d.lineheight()
  236.  
  237.  
  238. class BufferingStdwinFormatter(AbstractFormatter):
  239.     #
  240.     def init(self, left, top, right):
  241.         import stdwin
  242.         self.d = stdwin
  243.         self.buffer = []
  244.         self.curfont = None
  245.         return AbstractFormatter.init(self, left, top, right)
  246.     #
  247.     def d_text(self, point, text):
  248.         self.buffer.append('t', (point, text))
  249.     #
  250.     def d_setfont(self, font):
  251.         if font <> self.curfont:
  252.             self.curfont = font
  253.             self.d.setfont(font)
  254.             self.buffer.append('f', font)
  255.     #
  256.     def d_textwidth(self, text):
  257.         return self.d.textwidth(text)
  258.     #
  259.     def d_baseline(self):
  260.         return self.d.baseline()
  261.     #
  262.     def d_lineheight(self):
  263.         return self.d.lineheight()
  264.     #
  265.     def render(self, d, area):
  266.         (left, top), (right, bottom) = area
  267.         ntop = top - d.lineheight()
  268.         for command, args in self.buffer:
  269.             if command == 't':
  270.                 (h, v), text = args
  271.                 if v >= bottom:
  272.                     break
  273.                 if v > ntop and h < right:
  274.                     d.text(args)
  275.             else:
  276.                 d.setfont(args)
  277.                 ntop = top - d.lineheight()
  278.  
  279.  
  280. class GLFormatter(AbstractFormatter):
  281.     #
  282.     # NOTES:
  283.     # (1) Use gl.ortho2 to use X pixel coordinates!
  284.     # (2) Call setfont with some font handle immediately after initializing
  285.     #
  286.     def init(self, left, top, right):
  287.         self.fontkey = None
  288.         self.fonthandle = None
  289.         self.fontinfo = None
  290.         self.fontcache = {}
  291.         return AbstractFormatter.init(self, left, top, right)
  292.     #
  293.     def d_text(self, (h, v), text):
  294.         import gl, fm
  295.         gl.cmov2i(h, v + self.fontinfo[6] - self.fontinfo[3])
  296.         fm.prstr(text)
  297.     #
  298.     def d_setfont(self, fontkey):
  299.         if fontkey == '':
  300.             fontkey = 'Times-Roman 12'
  301.         elif ' ' not in fontkey:
  302.             fontkey = fontkey + ' 12'
  303.         if fontkey == self.fontkey:
  304.             return
  305.         if self.fontcache.has_key(fontkey):
  306.             handle = self.fontcache[fontkey]
  307.         else:
  308.             import string
  309.             i = string.index(fontkey, ' ')
  310.             name, sizestr = fontkey[:i], fontkey[i:]
  311.             size = eval(sizestr)
  312.             key1 = name + ' 1'
  313.             key = name + ' ' + `size`
  314.             # NB key may differ from fontkey!
  315.             if self.fontcache.has_key(key):
  316.                 handle = self.fontcache[key]
  317.             else:
  318.                 if self.fontcache.has_key(key1):
  319.                     handle = self.fontcache[key1]
  320.                 else:
  321.                     import fm
  322.                     handle = fm.findfont(name)
  323.                     self.fontcache[key1] = handle
  324.                 handle = handle.scalefont(size)
  325.                 self.fontcache[fontkey] = \
  326.                     self.fontcache[key] = handle
  327.         self.fontkey = fontkey
  328.         if self.fonthandle <> handle:
  329.             self.fonthandle = handle
  330.             self.fontinfo = handle.getfontinfo()
  331.             handle.setfont()
  332.     #
  333.     def d_textwidth(self, text):
  334.         return self.fonthandle.getstrwidth(text)
  335.     #
  336.     def d_baseline(self):
  337.         return self.fontinfo[6] - self.fontinfo[3]
  338.     #
  339.     def d_lineheight(self):
  340.         return self.fontinfo[6]
  341.